//: Playground - noun: a place where people can play

import UIKit

//： ### 讲 Int 型数组中每一个元素 +1
func incrementArray(xs: [Int]) -> [Int] {
    var result: [Int] = []
    for x in xs {
        result.append(x + 1)
    }
    return result
}

//: ### 将一个 Int 型数组中每一个元素 * 2
func doubleArray(xs: [Int]) -> [Int] {
    var result: [Int] = []
    for x in xs {
        result.append(x * 2)
    }
    return result
}

//: ### 上面代码很明显有很多重复代码

func computeInArray(xs: [Int], transform: Int -> Int) -> [Int] {
    var result: [Int] = []
    for x in xs {
        result.append(transform(x))
    }
    return result
}

//: ### 新的将 Int 型数组元素 * 2
func doubleArray2(xs: [Int]) -> [Int] {
    return computeInArray(xs) { (x) -> Int in
        x * 2
    }
}

//: ### 使用泛型

func genericComputeArray1<T>(xs: [Int], transform: Int -> T) -> [T] {
    var result: [T] = []
    for x in xs {
        result.append(transform(x))
    }
    return result
}

//: ### 对输入数组类型我们也不做限制 属于顶层函数

func map<Element, T>(xs: [Element], transform: Element -> T) -> [T] {
    var result: [T] = []
    for x in xs {
        result.append(transform(x))
    }
    return result
}

//: ### 扩展 推荐使用扩张，代码提示友好，减少暧昧函数名

extension Array {
    func map<T>(transform: Element -> T) -> [T] {
        var result: [T] = []
        for x in self {
            result.append(transform(x))
        }
        return result
    }
}

//: ### 通过 map 函数实例新的计算数组函数

func genericComputeArray2<T>(xs: [Int],transform: Int -> T) -> [T]  {
    return map(xs,transform: transform)
}

//: ## Filter

let exampleFiles = ["readme.md","helloworld.swift","flappybird.swift"]

//: ### 获取 swift 后缀名的文件

func getSwiftFiles(files: [String]) -> [String] {
    var result: [String] = []
    for file in files {
        if file.hasSuffix(".swift") {
            result.append(file)
        }
    }
    return result
}

getSwiftFiles(exampleFiles)


extension Array {
    func filter(includeElement: Element -> Bool) -> [Element] {
        var result: [Element] = []
        for x in self where includeElement(x) {
            result.append(x)
        }
        return result
    }
}

//: ### 重新定义 getSwiftFiles

func getSwiftFiles2(files: [String]) -> [String] {
    return files.filter{ file in file.hasSuffix(".swift") }
}

getSwiftFiles2(exampleFiles)

//: ## Reduce

//: ### 计算 Int 型数组所有元素之和

func sum(xs: [Int]) -> Int {
    var result: Int = 0
    for x in xs {
        result += x
    }
    return result
}


//: ### 计算 Int 型数组所有元素的乘积

func product(xs: [Int]) -> Int {
    var result: Int = 1
    for x in xs {
        result = x * result
    }
    return result
}

//: 另外我们可能还需要拼接一个 String 类型的数组

func concatenate(xs: [String]) -> String {
    var result: String = ""
    for x in xs {
        result += x
    }
    return result
}

concatenate(exampleFiles)

//: ### 扩展 Array 的 Reduce 函数

extension Array {
    func reduce<T>(initial: T, combine: (T, Element) -> T) -> T {
        var result = initial
        for x in self {
            result = combine(result, x)
        }
        return result
    }
}

exampleFiles.reduce("我是开头") { result, x in
    result + x
}

exampleFiles.reduce("我是开头", combine: +)

//: ### 利用 reduce 讲一个二维数组 展开成一个一维数组

func flattenUsingReduce<T>(xss: [[T]]) -> [T] {
    return xss.reduce([]){ result, xs in result + xs}
}

let exampleDoubleInt = [[1,2,3],[4,5,6],[7,8,9]]
flattenUsingReduce(exampleDoubleInt)

let i = [1,2,3] + [4,5,6]




//: ### 通过 reduce 重写 map

extension Array {
    func mapUsingReduce<T>(transform: Element -> T) -> [T] {
        return reduce([]){ (result, x) in
            return result + [transform(x)]
        }
    }
}


let exampleInt = [1,2,3,4]
genericComputeArray2(exampleInt) { (x) in
    2 + x
}

exampleInt.map { (x) in
    x + 10
}

exampleInt.mapUsingReduce { (x) in
    x + 3
}

//: ### 实际运用

struct City {
    let name: String
    let population: Int
}

let beijing = City(name: "北京", population: 1000)
let shanghai = City(name: "上海", population: 2040)
let shenzhen = City(name: "深圳", population: 1239)
let guangzhou = City(name: "广州", population: 294)

let cities = [beijing,shanghai,shenzhen,guangzhou]

extension City {
    
    func cityByScalingPopulation() -> City {
        return City(name: name, population: population * 1000)
    }
}
//: ### 定义一个筛选居民数量至少一百万的城市，并打印城市名跟人口
//: > 1. 先过滤用户大于 100万的城市
cities.filter { $0.population >= 1000 }
    //: > 2. 然后遍历数组将千居民转换为个位级用户
    .map{ $0.cityByScalingPopulation()}
    //: > 3. 遍历数组讲其拼接成指定的字符串
    .reduce("城市：人口"){result, c in
        return result + "\n" + "\(c.name):\(c.population)"
    }






